package com.bjpowernode.config;

import com.bjpowernode.config.filter.VerifyJwtFilter;
import com.bjpowernode.config.handler.MyAccessDeniedHandler;
import com.bjpowernode.config.handler.MyAuthenticationFailureHandler;
import com.bjpowernode.config.handler.MyAuthenticationSuccessHandler;
import com.bjpowernode.config.handler.MyLogoutSuccessHandler;
import com.bjpowernode.constant.Constants;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@EnableMethodSecurity //开启方法级别的权限检查
@Configuration
public class SecurityConfig {

    @Resource
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Resource
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    @Resource
    private MyAccessDeniedHandler myAccessDeniedHandler;

    @Resource
    private MyLogoutSuccessHandler myLogoutSuccessHandler;

    @Resource
    private VerifyJwtFilter verifyJwtFilter;

    //There is no PasswordEncoder mapped for the id "null"
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
        //return PasswordEncoderFactories.createDelegatingPasswordEncoder(); //底层默认就是bcrypt加密
    }

    /**
     * 配置一个Bean
     *
     * SecurityFilterChain 主要是配置spring security的行为
     *
     * 一旦你配置SecurityFilterChain这个Bean，那么spring security的默认行为就丢失了，
     * 如果你还想要有某个默认行为，你就需要在SecurityFilterChain这个Bean里面设置一下
     *
     * @return
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, CorsConfigurationSource configurationSource) throws Exception {
        //链式编程: 点方法，点方法......
        return httpSecurity
                .formLogin( (formLogin) -> {
                    formLogin.loginProcessingUrl(Constants.LOGIN_URI) //指定登录的处理地址是什么（这个地址不需要我们写controller方法，是框架处理的）
                            .usernameParameter("loginAct") //指定登录账号参数名是什么，默认参数名是：username
                            .passwordParameter("loginPwd") //指定登录密码参数名是什么，默认参数名是：password
                            .successHandler(myAuthenticationSuccessHandler) //登录成功后触发该handler，在该handler中返回json
                            .failureHandler(myAuthenticationFailureHandler); //登录失败后触发该handler，在该handler中返回json

                })

                //想要拦截所有接口路径这个行为，那我需要配置一下
                .authorizeHttpRequests( (authorize) -> {
                    authorize.requestMatchers("/api/toLogin", "/api/test", "/api/captcha").permitAll() //这两个地址不需要登录就能访问.
                            .anyRequest().authenticated(); //任何请求，都需要认证(登录)才能访问
                })

                .csrf( (csrf) -> {
                    csrf.disable(); //禁用跨站请求伪造的验证
                })

                //没有权限访问的配置（配置错误提示页）
                .exceptionHandling((exceptionHandling) -> {
                    exceptionHandling.accessDeniedHandler(myAccessDeniedHandler); //没有权限访问触发该handler，在该handler中返回json
                })

                //配置退出的功能
                .logout((logout) -> {
                    logout.logoutUrl("/api/logout") //退出登录的处理地址是什么 （这个地址不需要我们写controller方法，是框架处理的）
                            .logoutSuccessHandler(myLogoutSuccessHandler); //退出成功触发该handler，在该handler中返回json
                })

                //配置session管理
                .sessionManagement( (sessionManagement) -> {
                    sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS); //无session状态，也就是不创建session
                } )

                //配置验证jwt的Filter
                .addFilterBefore(verifyJwtFilter, LogoutFilter.class)

                //配置跨域 （我们配置了spring security支持跨域后，spring boot的controller也支持跨域）
                .cors( (cors) -> {
                    cors.configurationSource(configurationSource);
                })

                .build();
    }

    /**
     * 配置spring security跨域支持的Bean
     *
     * @return
     */
    @Bean
    public CorsConfigurationSource configurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowedOrigins(Arrays.asList("*")); //允许任何源头，不管来自于哪个域名的请求，都可以请求
        configuration.setAllowedMethods(Arrays.asList("*")); //允许任何方法，不管是post、get、put、delete，都可以请求
        configuration.setAllowedHeaders(Arrays.asList("*")); //允许任何请求头，不管请求头中设置的是什么信息，都可以请求

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
